package sip.oshima.ua;

import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.peers.Config;
import net.sourceforge.peers.JavaConfig;
import net.sourceforge.peers.Slf4jLogger;
import net.sourceforge.peers.javaxsound.JavaxSoundManager;
import net.sourceforge.peers.media.AbstractSoundManager;
import net.sourceforge.peers.media.MediaMode;
import net.sourceforge.peers.sip.RFC3261;
import net.sourceforge.peers.sip.Utils;
import net.sourceforge.peers.sip.core.useragent.SipListener;
import net.sourceforge.peers.sip.core.useragent.UserAgent;
import net.sourceforge.peers.sip.syntaxencoding.SipHeaderFieldName;
import net.sourceforge.peers.sip.syntaxencoding.SipHeaders;
import net.sourceforge.peers.sip.syntaxencoding.SipUriSyntaxException;
import net.sourceforge.peers.sip.transactionuser.Dialog;
import net.sourceforge.peers.sip.transactionuser.DialogManager;
import net.sourceforge.peers.sip.transport.SipRequest;
import net.sourceforge.peers.sip.transport.SipResponse;
import sip.oshima.ua.domain.SipUserInfo;
import sip.oshima.ua.websocket.WebSocketServer;

import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Optional;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Data
@Slf4j
public class SipClient implements SipListener {

    private static SipClient instance;
    private UserAgent userAgent;
    private static ScheduledExecutorService expireRegister = Executors.newScheduledThreadPool(1);
    private static ScheduledFuture<?> expireFuture;
    private SipRequest currentSipRequest;
/*

    public static void main(String[] args) {
        SipClient sipClient = SipClient.getInstance();
        sipClient.createUserAgent("104", "104", "192.168.110.129:6060");
    }
*/

    private SipClient() {
    }

    /**
     * 单例模式，全局只能有一个用户实例 UA
     *
     * @return SipClient
     */
    public static synchronized SipClient getInstance() {
        if (instance == null) {
            instance = new SipClient();
        }
        return instance;
    }

    public void createUserAgent(SipUserInfo userInfo) {
        createUserAgent(userInfo.getNumber(), userInfo.getPwd(), userInfo.getServer());
    }

    public void createUserAgent(String number, String pwd, String sipServer) {
        log.info("开始注册用户代理");
        Optional.ofNullable(expireFuture).ifPresent(expireFuture-> expireFuture.cancel(true));
        WebSocketServer.sendMsg("msg", "开始注册用户代理");
        if (userAgent != null && userAgent.isRegistered()) {
            log.warn("已经存在用户代理，下线：{}", userAgent.getUserpart());
            WebSocketServer.sendMsg("msg", "已经存在用户代理，注销其他代理");
            this.destroy();
        }
        String peersHome = Utils.DEFAULT_PEERS_HOME;
        Slf4jLogger logger = new Slf4jLogger();
        final AbstractSoundManager soundManager = new JavaxSoundManager(false, logger, peersHome);
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            Config config = new JavaConfig();
            config.setUserPart(number);
            config.setDomain(sipServer);
            config.setPassword(pwd);
            config.setMediaMode(MediaMode.captureAndPlayback);
            config.setLocalInetAddress(localHost);
            userAgent = new UserAgent(this, config, logger, soundManager);
            try {
                userAgent.register();
            } catch (SipUriSyntaxException e) {
                throw new RuntimeException(e);
            }
            log.warn("发起注册");
            WebSocketServer.sendMsg("msg", "发起注册");
        } catch (SocketException | UnknownHostException e) {
            throw new RuntimeException(e);
        }
    }

    public void destroy() {
        if (this.userAgent != null) {
            try {
                log.debug("注销代理");
                Optional.ofNullable(expireFuture).ifPresent(expireFuture-> expireFuture.cancel(true));
                this.userAgent.unregister();
            } catch (SipUriSyntaxException e) {
                log.error("注销代理失败，{}", e.getMessage());
                WebSocketServer.sendMsg("msg", "注销代理失败");
            }
        }
    }

    @Override
    public void registering(SipRequest sipRequest) {
        log.warn("registering");
        WebSocketServer.sendMsg("code", "registering", "msg", "注册中");
    }

    @Override
    public void registerSuccessful(SipResponse sipResponse) {
        // 注册过期时间
        String expires = "";

        try {
            expires = sipResponse.getSipHeaders().get(new SipHeaderFieldName(RFC3261.HDR_EXPIRES)).getValue();
        } catch (Exception e) {
            log.debug("过期时间为空");
        }
        if (StrUtil.isNotBlank(expires)) {
            long expire = Long.parseLong(expires);
            if (expire == 0) {
                WebSocketServer.sendMsg("code", "unregisterSuccessful", "msg", "注销成功");
            } else {
                WebSocketServer.sendMsg("code", "registerSuccessful", "msg", "注册成功");
                log.debug("注册自动续期,expire:{}", expire);
                // 自动续期
                Optional.ofNullable(expireFuture).ifPresent(expireFuture-> expireFuture.cancel(true));
                expireFuture = expireRegister.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            log.debug("开始自动续期");
                            if (userAgent != null && userAgent.isRegistered()) {
                                WebSocketServer.sendMsg("msg", "开始自动续期");
                                userAgent.register();
                            }
                        } catch (SipUriSyntaxException e) {
                            log.error(e.getMessage());
                            WebSocketServer.sendMsg("code", "expireRegisterFailed", "msg", "自动续期失败");
                        }
                    }
                }, expire, TimeUnit.SECONDS);
            }
        }
        else {
//            WebSocketServer.sendMsg("code", "noExpires", "msg", "无过期时间");
        }
    }

    @Override
    public void registerFailed(SipResponse sipResponse) {
        log.warn("registerFailed");
        WebSocketServer.sendMsg("code", "registerFailed", "msg", "注册失败");
    }

    @Override
    public void incomingCall(SipRequest sipRequest, SipResponse provResponse) {
        SipHeaders sipHeaders = sipRequest.getSipHeaders();
        String headFrom = sipHeaders.get(new SipHeaderFieldName(RFC3261.HDR_FROM)).getValue();
        String tempNumber = headFrom.substring(headFrom.indexOf("<") + 5, headFrom.indexOf("@"));
        String fromNumber = "";
        if (!tempNumber.equals("unknown")) fromNumber = tempNumber;
        // "MonitorChannel" <sip:unknown@192.168.2.150>
        String displayName = fromNumber;
        if (headFrom.startsWith("\"")) {
            displayName = headFrom.substring(1, headFrom.lastIndexOf("\""));
        }
        log.warn("来电：{}\tdisplayName：{}", fromNumber, displayName);

        WebSocketServer.sendMsg("code", "incomingCall", "msg", "新来电", "fromNumber", fromNumber, "displayName", displayName);
        currentSipRequest = sipRequest;
        /*
         */
    }

    public void pickup() {
        if (currentSipRequest == null) {
            WebSocketServer.sendMsg("msg", "当前无等待接听电话");
            return;
        }
        String callId = Utils.getMessageCallId(currentSipRequest);
        DialogManager dialogManager = userAgent.getDialogManager();
        Dialog dialog = dialogManager.getDialog(callId);
        userAgent.acceptCall(currentSipRequest, dialog);
        WebSocketServer.sendMsg("code", "pickup", "msg", "已接通");
    }

    public void hangup() {
        if (currentSipRequest == null) {
            WebSocketServer.sendMsg("msg", "当前无已接听电话");
            return;
        }
        userAgent.terminate(currentSipRequest);
        WebSocketServer.sendMsg("code", "hangup", "msg", "已挂断");
        currentSipRequest = null;
    }

    public void call(String number) {
        if (StrUtil.isBlank(number)) {
            WebSocketServer.sendMsg("msg", "号码为空");
            return;
        }
        try {
            currentSipRequest = userAgent.invite("sip:" + number + "@" + userAgent.getDomain(), Utils.generateCallID(userAgent.getConfig().getLocalInetAddress()));
            WebSocketServer.sendMsg("code", "invite", "msg", "已发起呼叫");
        } catch (SipUriSyntaxException e) {
            log.error(e.getMessage());
            WebSocketServer.sendMsg("code", "inviteFailed", "msg", "呼叫失败", "error", e.getMessage());
        }
    }

    @Override
    public void remoteHangup(SipRequest sipRequest) {
        log.warn("remoteHangup");
        WebSocketServer.sendMsg("code", "remoteHangup", "msg", "对方挂断");
        currentSipRequest = null;
    }

    @Override
    public void ringing(SipResponse sipResponse) {
        log.warn("ringing");
        WebSocketServer.sendMsg("code", "ringing", "msg", "振铃中");
    }

    @Override
    public void calleePickup(SipResponse sipResponse) {
        log.warn("calleePickup");
        WebSocketServer.sendMsg("code", "calleePickup", "msg", "已接通");
    }

    @Override
    public void error(SipResponse sipResponse) {
        log.warn("error:{}",sipResponse.getReasonPhrase());
        if("Request Timeout".equals(sipResponse.getReasonPhrase())){
            WebSocketServer.sendMsg("code", "remoteHangup",
                    "msg","对方挂断,或者无法接通",
                    "statusCode", String.valueOf(sipResponse.getStatusCode()));
        }else{
            WebSocketServer.sendMsg("code", "error",
                    "msg",sipResponse.getReasonPhrase(),
                    "statusCode", String.valueOf(sipResponse.getStatusCode()));
        }

        currentSipRequest = null;
    }
}
